<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Vue3 图片瀑布流</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/3.3.4/vue.global.min.js"></script>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            min-height: 100vh;
        }

        .container {
            max-width: 1200px;
            margin: 0 auto;
            padding: 20px;
        }

        .title {
            text-align: center;
            color: white;
            font-size: 2.5rem;
            font-weight: bold;
            margin-bottom: 30px;
            text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
        }

        .waterfall-container {
            display: flex;
            gap: 15px;
            justify-content: center;
            align-items: flex-start;
        }

        .waterfall-column {
            display: flex;
            flex-direction: column;
            gap: 15px;
        }

        .image-card {
            background: white;
            border-radius: 12px;
            overflow: hidden;
            box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
            transition: all 0.3s ease;
            cursor: pointer;
        }

        .image-card:hover {
            transform: translateY(-5px) scale(1.02);
            box-shadow: 0 8px 30px rgba(0, 0, 0, 0.2);
        }

        .image-card img {
            width: 100%;
            height: auto;
            display: block;
            transition: transform 0.3s ease;
        }

        .image-card:hover img {
            transform: scale(1.05);
        }

        .card-content {
            padding: 15px;
        }

        .card-title {
            font-size: 1.1rem;
            font-weight: 600;
            color: #333;
            margin-bottom: 8px;
        }

        .card-description {
            color: #666;
            font-size: 0.9rem;
            line-height: 1.4;
            margin-bottom: 12px;
        }

        .card-footer {
            display: flex;
            justify-content: space-between;
            align-items: center;
        }

        .card-id {
            font-size: 0.8rem;
            color: #999;
        }

        .card-actions {
            display: flex;
            gap: 10px;
        }

        .action-btn {
            background: none;
            border: none;
            cursor: pointer;
            padding: 5px;
            border-radius: 50%;
            transition: all 0.2s ease;
        }

        .action-btn:hover {
            background: rgba(0, 0, 0, 0.1);
            transform: scale(1.1);
        }

        .like-btn {
            color: #ff6b6b;
        }

        .share-btn {
            color: #4ecdc4;
        }

        .loading {
            display: flex;
            justify-content: center;
            align-items: center;
            padding: 30px;
            color: white;
        }

        .spinner {
            width: 30px;
            height: 30px;
            border: 3px solid rgba(255, 255, 255, 0.3);
            border-radius: 50%;
            border-top-color: white;
            animation: spin 1s ease-in-out infinite;
            margin-right: 10px;
        }

        @keyframes spin {
            to {
                transform: rotate(360deg);
            }
        }

        .load-more-btn {
            display: block;
            margin: 30px auto;
            padding: 12px 30px;
            background: rgba(255, 255, 255, 0.2);
            color: white;
            border: 2px solid white;
            border-radius: 25px;
            font-size: 1rem;
            cursor: pointer;
            transition: all 0.3s ease;
            backdrop-filter: blur(10px);
        }

        .load-more-btn:hover {
            background: white;
            color: #667eea;
            transform: translateY(-2px);
        }

        .load-more-btn:disabled {
            opacity: 0.5;
            cursor: not-allowed;
        }

        .info-text {
            text-align: center;
            color: rgba(255, 255, 255, 0.8);
            margin-top: 30px;
            font-size: 0.9rem;
        }

        /* 响应式设计 */
        @media (max-width: 768px) {
            .waterfall-column {
                width: calc(50% - 7.5px);
            }

            .title {
                font-size: 2rem;
            }
        }

        @media (min-width: 769px) and (max-width: 1024px) {
            .waterfall-column {
                width: calc(33.333% - 10px);
            }
        }

        @media (min-width: 1025px) {
            .waterfall-column {
                width: calc(25% - 11.25px);
            }
        }
    </style>
</head>

<body>
    <div id="app">
        <div class="container">
            <h1 class="title">Vue3 图片瀑布流</h1>

            <div class="waterfall-container" ref="waterfallContainer">
                <div v-for="(column, index) in columnImages" :key="index" class="waterfall-column">
                    <div v-for="image in column" :key="image.id" class="image-card" @click="handleImageClick(image)">
                        <img :src="image.src" :alt="image.alt" loading="lazy" @load="handleImageLoad" />
                        <div class="card-content">
                            <h3 class="card-title">{{ image.title }}</h3>
                            <p class="card-description">{{ image.description }}</p>
                            <div class="card-footer">
                                <span class="card-id">ID: {{ image.id }}</span>
                                <div class="card-actions">
                                    <button class="action-btn like-btn" @click.stop="toggleLike(image)">
                                        <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
                                            <path
                                                d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z" />
                                        </svg>
                                    </button>
                                    <button class="action-btn share-btn" @click.stop="shareImage(image)">
                                        <svg width="16" height="16" viewBox="0 0 24 24" fill="none"
                                            stroke="currentColor" stroke-width="2">
                                            <circle cx="18" cy="5" r="3" />
                                            <circle cx="6" cy="12" r="3" />
                                            <circle cx="18" cy="19" r="3" />
                                            <line x1="8.59" y1="13.51" x2="15.42" y2="17.49" />
                                            <line x1="15.41" y1="6.51" x2="8.59" y2="10.49" />
                                        </svg>
                                    </button>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>

            <!-- 加载指示器 -->
            <div v-if="loading" class="loading">
                <div class="spinner"></div>
                <span>正在加载更多图片...</span>
            </div>

            <!-- 加载更多按钮 -->
            <button class="load-more-btn" @click="loadMore" :disabled="loading">
                {{ loading ? '加载中...' : '加载更多' }}
            </button>

            <div class="info-text">
                📱 响应式设计 | 🔄 无限滚动 | ✨ 悬停效果 | 🎨 Vue3 Composition API
            </div>
        </div>
    </div>

    <script>
        const { createApp, ref, computed, onMounted, onUnmounted, nextTick } = Vue;

        createApp({
            setup() {
                // 响应式数据
                const loading = ref(false);
                const columns = ref(4);
                const waterfallContainer = ref(null);

                // 模拟图片数据
                const allImages = [
                    { id: 1, src: 'https://picsum.photos/300/400?random=1', alt: '美丽风景1', title: '梦幻日落', description: '夕阳西下，天空被染成了金黄色，美得让人心醉。' },
                    { id: 2, src: 'https://picsum.photos/300/600?random=2', alt: '美丽风景2', title: '静谧湖泊', description: '清晨的湖面如镜，倒映着远山和白云。' },
                    { id: 3, src: 'https://picsum.photos/300/350?random=3', alt: '美丽风景3', title: '春日花海', description: '满山遍野的花朵，春天的气息扑面而来。' },
                    { id: 4, src: 'https://picsum.photos/300/500?random=4', alt: '美丽风景4', title: '城市夜景', description: '繁华都市的夜晚，霓虹灯闪烁着迷人的光芒。' },
                    { id: 5, src: 'https://picsum.photos/300/450?random=5', alt: '美丽风景5', title: '森林小径', description: '蜿蜒的小路通向森林深处，充满神秘感。' },
                    { id: 6, src: 'https://picsum.photos/300/550?random=6', alt: '美丽风景6', title: '海边黄昏', description: '海浪轻拍着沙滩，远处的夕阳缓缓沉入海平线。' },
                    { id: 7, src: 'https://picsum.photos/300/380?random=7', alt: '美丽风景7', title: '雪山之巅', description: '白雪皑皑的山峰直插云霄，展现大自然的壮美。' },
                    { id: 8, src: 'https://picsum.photos/300/480?random=8', alt: '美丽风景8', title: '田园风光', description: '金黄的麦田随风摆动，宁静的乡村生活。' },
                    { id: 9, src: 'https://picsum.photos/300/420?random=9', alt: '美丽风景9', title: '瀑布奇观', description: '飞流直下三千尺，瀑布的壮观令人震撼。' },
                    { id: 10, src: 'https://picsum.photos/300/520?random=10', alt: '美丽风景10', title: '秋日枫叶', description: '满树的红叶如火如荼，秋天的浪漫诗意。' },
                    { id: 11, src: 'https://picsum.photos/300/360?random=11', alt: '美丽风景11', title: '沙漠驼铃', description: '广袤的沙漠中，驼队缓缓前行，留下长长的足迹。' },
                    { id: 12, src: 'https://picsum.photos/300/440?random=12', alt: '美丽风景12', title: '樱花飞舞', description: '粉色的樱花瓣如雪花般飘落，浪漫至极。' },
                    { id: 13, src: 'https://picsum.photos/300/380?random=13', alt: '美丽风景13', title: '极光之夜', description: '绚烂的极光在夜空中舞蹈，如梦如幻。' },
                    { id: 14, src: 'https://picsum.photos/300/500?random=14', alt: '美丽风景14', title: '古镇水乡', description: '小桥流水人家，江南水乡的温柔情调。' },
                    { id: 15, src: 'https://picsum.photos/300/460?random=15', alt: '美丽风景15', title: '云海仙境', description: '云雾缭绕的山峰，如同人间仙境一般。' },
                    { id: 16, src: 'https://picsum.photos/300/410?random=16', alt: '美丽风景16', title: '星空银河', description: '璀璨的银河横跨夜空，星光点点如钻石般闪烁。' }
                ];

                const imageList = ref(allImages.slice(0, 8));

                // 计算列数
                const updateColumns = () => {
                    const width = window.innerWidth;
                    if (width < 768) columns.value = 2;
                    else if (width < 1024) columns.value = 3;
                    else columns.value = 4;
                };

                // 计算瀑布流布局
                const columnImages = computed(() => {
                    const cols = Array.from({ length: columns.value }, () => []);
                    const heights = Array(columns.value).fill(0);

                    imageList.value.forEach((img) => {
                        const shortestCol = heights.indexOf(Math.min(...heights));
                        cols[shortestCol].push(img);
                        // 模拟图片高度计算
                        heights[shortestCol] += 300 + Math.random() * 200;
                    });

                    return cols;
                });

                // 加载更多图片
                const loadMore = () => {
                    if (loading.value) return;

                    loading.value = true;

                    setTimeout(() => {
                        const currentLength = imageList.value.length;
                        const nextImages = allImages.slice(currentLength, currentLength + 4);

                        if (nextImages.length > 0) {
                            imageList.value.push(...nextImages);
                        }

                        loading.value = false;
                    }, 1000);
                };

                // 无限滚动
                const handleScroll = () => {
                    if (loading.value) return;

                    const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
                    const windowHeight = window.innerHeight;
                    const documentHeight = document.documentElement.scrollHeight;

                    if (scrollTop + windowHeight >= documentHeight - 1000) {
                        loadMore();
                    }
                };

                // 事件处理
                const handleImageClick = (image) => {
                    console.log('点击图片:', image);
                };

                const handleImageLoad = () => {
                    console.log('图片加载完成');
                };

                const toggleLike = (image) => {
                    console.log('点赞图片:', image);
                };

                const shareImage = (image) => {
                    console.log('分享图片:', image);
                };

                // 生命周期
                onMounted(() => {
                    updateColumns();
                    window.addEventListener('resize', updateColumns);
                    window.addEventListener('scroll', handleScroll);
                });

                onUnmounted(() => {
                    window.removeEventListener('resize', updateColumns);
                    window.removeEventListener('scroll', handleScroll);
                });

                return {
                    loading,
                    columns,
                    imageList,
                    columnImages,
                    waterfallContainer,
                    loadMore,
                    handleImageClick,
                    handleImageLoad,
                    toggleLike,
                    shareImage
                };
            }
        }).mount('#app');
    </script>
</body>

</html>